#include "Sniffer.h"

//===========================================================================
//
// Constructor:
//
//		Parameters: queue - pointer to the sniffed packets queue
//
Sniffer::Sniffer(SharedQueue<struct RAWIPDATA>* queue)
{
	_snifferQ=queue;
	_numSniffedPackets=0;
	_errorPacketRead=0;
	_snifferRestarts=0;
	_pckCapDesc=NULL;
	config();
}


//===========================================================================
//
// Desctructor
//
Sniffer::~Sniffer()
{
}
//===========================================================================
//
// config(): reading sniffer configuration from the sniffer conf file
//
void Sniffer::config()
{
	const string conf_file="/opt/swaf/conf/sniffer.conf";

	try
	{
	ConfigFile cf(conf_file);
	_ws_ip.assign(cf.Value("web-server","ip"));
   	_ws_port.assign(cf.Value("web-server","port"));
   	_interface.assign(cf.Value("interface","ethX"));

	// Setting filter capture string
	_pcapFilter.append("dst host ") ;
	_pcapFilter.append(_ws_ip);
	_pcapFilter.append(" and tcp port ");
	_pcapFilter.append(_ws_port);

	}
	catch(const char *e)
	{
	        cout <<"\nError --> Failed to read Sniffer configuration."<<endl;
			cout <<"Check configuration file: "<<conf_file<<"\n"<<endl;
			exit(1);
	}

}


//===========================================================================
//
// init(): set the sniffer configuration
//
void Sniffer::init()
{
        const char *ifname;            /* _interface name (such as "eth0") */
        char errbuf[PCAP_ERRBUF_SIZE];  /* buffer to hold error text */
        char lhost[MAXHOSTSZ];   /* local host name */
        char fltstr[FLTRSZ];     /* bpf filter string */
        char prestr[80];         /* prefix string for errors from pcap_perror */
        struct bpf_program prog; /* compiled bpf filter program */
        int optimize = 1;        /* passed to pcap_compile to do optimization */
        int snaplen = 1000;        /* amount of data per packet */
        int promisc = 0;         /* do not change mode; if in promiscuous */
                                 /* mode, stay in it, otherwise, do not */
        int to_ms = 1000;        /* timeout, in milliseconds */
        int count = 1000;          /* number of packets to capture */
        u_int net = 0;         /* network IP address */
        u_int mask = 0;        /* network address mask */
        char netstr[INET_ADDRSTRLEN];   /* dotted decimal form of address */
        char maskstr[INET_ADDRSTRLEN];  /* dotted decimal form of net mask */

		ifname=_interface.c_str();

        if (!(_pckCapDesc = pcap_open_live(ifname, snaplen, promisc, to_ms, errbuf))) {
                fprintf(stderr,
                        "Error opening _interface %s: %s\n", ifname, errbuf);
                exit(2);
        }

   if (pcap_lookupnet(ifname,(bpf_u_int32*)&net,(bpf_u_int32*)&mask,errbuf) < 0) {
                  fprintf(stderr, "Error looking up network: %s\n", errbuf);
                   exit(3);
           }


        if (gethostname(lhost,sizeof(lhost)) < 0) {
                fprintf(stderr, "Error getting hostname.\n");
                exit(4);
        }

        inet_ntop(AF_INET, (char*) &net, netstr, sizeof netstr);
        inet_ntop(AF_INET, (char*) &mask, maskstr, sizeof maskstr);



		_pcapFilter="port 80"; // for debug only!
		sprintf(fltstr,_pcapFilter.c_str(),lhost, netstr, maskstr);

		#ifdef DEBUG_SNIFFER
		cout<<" pcapFilter = "<<_pcapFilter<<endl;
		#endif


        if (pcap_compile(_pckCapDesc,&prog,fltstr,optimize,mask) < 0) {

                fprintf(stderr, "Error compiling bpf filter on %s: %s\n",
                        ifname, pcap_geterr(_pckCapDesc));
                exit(1);
        }

        if (pcap_setfilter(_pckCapDesc, &prog) < 0) {
                sprintf(prestr, "Error installing bpf filter on _interface %s",
                        ifname);
                pcap_perror(_pckCapDesc,prestr);
                exit(1);
        }
}

//===========================================================================
//
// runSniffer() - the main sniffer method:
// 					pcap_next_ex() runs in endless loop
//					handlePacket() is called upon each sniffed packet
//
void Sniffer::runSniffer()
{
	char prestr[80];
	struct pcap_pkthdr *header;
	const u_char *packet;

	while(1) // main loop
	{
        if ( pcap_next_ex(_pckCapDesc, &header, &packet) < 0 ) // incase of sniffer reading error
        {
			sprintf(prestr,"Error reading packets from _interface %s",_interface.c_str());
			pcap_perror(_pckCapDesc,prestr);

			_errorPacketRead++;

			//incase sniffing errors reached NEED_TO_RESTART_VAL --> break the main loop
			if(NEED_TO_RESTART_VAL==_errorPacketRead)
				break;
		}
		else // handle the sniffed packet
		{
			_numSniffedPackets++;
			handlePacket(header,packet);
		}
	} //while

	restartSnifferRun();
}


//===========================================================================
//
// handlePacket() - handles each sniffed packet
//
// 					Parameters: pkthdr - pointer to the pcap_pkthdr struct
//								data - pointer to the captured packet data
//
void Sniffer::handlePacket(const struct pcap_pkthdr* pkthdr,const u_char* sniffedRawData)
{
	struct RAWIPDATA raw;

	raw.datalen=(pkthdr->caplen-IP_HEADER_OFFSET);
	sniffedRawData+=IP_HEADER_OFFSET;
	memcpy(raw.data,sniffedRawData,raw.datalen);

	//Insert the RawIpData to the sniffer queue
	_snifferQ->push(raw);
}

//===========================================================================
//
// restartSnifferRun() - restart the sniffer incase of too many sniffing errors
//
void Sniffer::restartSnifferRun()
{
	pcap_close(_pckCapDesc);
	_errorPacketRead=0;
	_snifferRestarts++;

	if (_snifferRestarts==MAX_SNIFFER_RESTARTS)
	{
		// write to log --> Problem with sniffer
		exit(1);
	}

	init();
	runSniffer();
}
